// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/log/net_log.h"

#include <utility>

#include "base/bind.h"
#include "base/debug/alias.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "net/base/net_errors.h"

namespace net {

namespace {

    // Returns parameters for logging data transferred events. At a minimum includes
    // the number of bytes transferred. If the capture mode allows logging byte
    // contents and |byte_count| > 0, then will include the actual bytes. The
    // bytes are hex-encoded, since base::StringValue only supports UTF-8.
    std::unique_ptr<base::Value> BytesTransferredCallback(
        int byte_count,
        const char* bytes,
        NetLogCaptureMode capture_mode)
    {
        std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
        dict->SetInteger("byte_count", byte_count);
        if (capture_mode.include_socket_bytes() && byte_count > 0)
            dict->SetString("hex_encoded_bytes", base::HexEncode(bytes, byte_count));
        return std::move(dict);
    }

    std::unique_ptr<base::Value> SourceEventParametersCallback(
        const NetLog::Source source,
        NetLogCaptureMode /* capture_mode */)
    {
        if (!source.IsValid())
            return std::unique_ptr<base::Value>();
        std::unique_ptr<base::DictionaryValue> event_params(
            new base::DictionaryValue());
        source.AddToEventParameters(event_params.get());
        return std::move(event_params);
    }

    std::unique_ptr<base::Value> NetLogBoolCallback(
        const char* name,
        bool value,
        NetLogCaptureMode /* capture_mode */)
    {
        std::unique_ptr<base::DictionaryValue> event_params(
            new base::DictionaryValue());
        event_params->SetBoolean(name, value);
        return std::move(event_params);
    }

    std::unique_ptr<base::Value> NetLogIntCallback(
        const char* name,
        int value,
        NetLogCaptureMode /* capture_mode */)
    {
        std::unique_ptr<base::DictionaryValue> event_params(
            new base::DictionaryValue());
        event_params->SetInteger(name, value);
        return std::move(event_params);
    }

    std::unique_ptr<base::Value> NetLogInt64Callback(
        const char* name,
        int64_t value,
        NetLogCaptureMode /* capture_mode */)
    {
        std::unique_ptr<base::DictionaryValue> event_params(
            new base::DictionaryValue());
        event_params->SetString(name, base::Int64ToString(value));
        return std::move(event_params);
    }

    std::unique_ptr<base::Value> NetLogStringCallback(
        const char* name,
        const std::string* value,
        NetLogCaptureMode /* capture_mode */)
    {
        std::unique_ptr<base::DictionaryValue> event_params(
            new base::DictionaryValue());
        event_params->SetString(name, *value);
        return std::move(event_params);
    }

    std::unique_ptr<base::Value> NetLogCharStringCallback(
        const char* name,
        const char* value,
        NetLogCaptureMode /* capture_mode */)
    {
        std::unique_ptr<base::DictionaryValue> event_params(
            new base::DictionaryValue());
        event_params->SetString(name, value);
        return std::move(event_params);
    }

    std::unique_ptr<base::Value> NetLogString16Callback(
        const char* name,
        const base::string16* value,
        NetLogCaptureMode /* capture_mode */)
    {
        std::unique_ptr<base::DictionaryValue> event_params(
            new base::DictionaryValue());
        event_params->SetString(name, *value);
        return std::move(event_params);
    }

} // namespace

// LoadTimingInfo requires this be 0.
const uint32_t NetLog::Source::kInvalidId = 0;

NetLog::Source::Source()
    : type(SOURCE_NONE)
    , id(kInvalidId)
{
}

NetLog::Source::Source(SourceType type, uint32_t id)
    : type(type)
    , id(id)
{
}

bool NetLog::Source::IsValid() const
{
    return id != kInvalidId;
}

void NetLog::Source::AddToEventParameters(
    base::DictionaryValue* event_params) const
{
    std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
    dict->SetInteger("type", static_cast<int>(type));
    dict->SetInteger("id", static_cast<int>(id));
    event_params->Set("source_dependency", std::move(dict));
}

NetLog::ParametersCallback NetLog::Source::ToEventParametersCallback() const
{
    return base::Bind(&SourceEventParametersCallback, *this);
}

// static
bool NetLog::Source::FromEventParameters(base::Value* event_params,
    Source* source)
{
    base::DictionaryValue* dict = NULL;
    base::DictionaryValue* source_dict = NULL;
    int source_id = -1;
    int source_type = NetLog::SOURCE_COUNT;
    if (!event_params || !event_params->GetAsDictionary(&dict) || !dict->GetDictionary("source_dependency", &source_dict) || !source_dict->GetInteger("id", &source_id) || !source_dict->GetInteger("type", &source_type)) {
        *source = Source();
        return false;
    }

    DCHECK_GE(source_id, 0);
    DCHECK_LT(source_type, NetLog::SOURCE_COUNT);
    *source = Source(static_cast<SourceType>(source_type), source_id);
    return true;
}

std::unique_ptr<base::Value> NetLog::Entry::ToValue() const
{
    std::unique_ptr<base::DictionaryValue> entry_dict(
        new base::DictionaryValue());

    entry_dict->SetString("time", TickCountToString(data_->time));

    // Set the entry source.
    std::unique_ptr<base::DictionaryValue> source_dict(
        new base::DictionaryValue());
    source_dict->SetInteger("id", data_->source.id);
    source_dict->SetInteger("type", static_cast<int>(data_->source.type));
    entry_dict->Set("source", std::move(source_dict));

    // Set the event info.
    entry_dict->SetInteger("type", static_cast<int>(data_->type));
    entry_dict->SetInteger("phase", static_cast<int>(data_->phase));

    // Set the event-specific parameters.
    if (data_->parameters_callback) {
        std::unique_ptr<base::Value> value(
            data_->parameters_callback->Run(capture_mode_));
        if (value)
            entry_dict->Set("params", std::move(value));
    }

    return std::move(entry_dict);
}

std::unique_ptr<base::Value> NetLog::Entry::ParametersToValue() const
{
    if (data_->parameters_callback)
        return data_->parameters_callback->Run(capture_mode_);
    return nullptr;
}

NetLog::EntryData::EntryData(EventType type,
    Source source,
    EventPhase phase,
    base::TimeTicks time,
    const ParametersCallback* parameters_callback)
    : type(type)
    , source(source)
    , phase(phase)
    , time(time)
    , parameters_callback(parameters_callback)
{
}

NetLog::EntryData::~EntryData()
{
}

NetLog::Entry::Entry(const EntryData* data, NetLogCaptureMode capture_mode)
    : data_(data)
    , capture_mode_(capture_mode)
{
}

NetLog::Entry::~Entry()
{
}

NetLog::ThreadSafeObserver::ThreadSafeObserver()
    : net_log_(NULL)
{
}

NetLog::ThreadSafeObserver::~ThreadSafeObserver()
{
    // Make sure we aren't watching a NetLog on destruction.  Because the NetLog
    // may pass events to each observer on multiple threads, we cannot safely
    // stop watching a NetLog automatically from a parent class.
    DCHECK(!net_log_);
}

NetLogCaptureMode NetLog::ThreadSafeObserver::capture_mode() const
{
    DCHECK(net_log_);
    return capture_mode_;
}

NetLog* NetLog::ThreadSafeObserver::net_log() const
{
    return net_log_;
}

void NetLog::ThreadSafeObserver::OnAddEntryData(const EntryData& entry_data)
{
    OnAddEntry(Entry(&entry_data, capture_mode()));
}

NetLog::NetLog()
    : last_id_(0)
    , is_capturing_(0)
{
}

NetLog::~NetLog()
{
}

void NetLog::AddGlobalEntry(EventType type)
{
    AddEntry(type, Source(NetLog::SOURCE_NONE, NextID()), NetLog::PHASE_NONE,
        NULL);
}

void NetLog::AddGlobalEntry(
    EventType type,
    const NetLog::ParametersCallback& parameters_callback)
{
    AddEntry(type, Source(NetLog::SOURCE_NONE, NextID()), NetLog::PHASE_NONE,
        &parameters_callback);
}

uint32_t NetLog::NextID()
{
    return base::subtle::NoBarrier_AtomicIncrement(&last_id_, 1);
}

bool NetLog::IsCapturing() const
{
    return base::subtle::NoBarrier_Load(&is_capturing_) != 0;
}

void NetLog::DeprecatedAddObserver(NetLog::ThreadSafeObserver* observer,
    NetLogCaptureMode capture_mode)
{
    base::AutoLock lock(lock_);

    DCHECK(!observer->net_log_);
    observers_.AddObserver(observer);
    observer->net_log_ = this;
    observer->capture_mode_ = capture_mode;
    UpdateIsCapturing();
}

void NetLog::SetObserverCaptureMode(NetLog::ThreadSafeObserver* observer,
    NetLogCaptureMode capture_mode)
{
    base::AutoLock lock(lock_);

    DCHECK(observers_.HasObserver(observer));
    DCHECK_EQ(this, observer->net_log_);
    observer->capture_mode_ = capture_mode;
}

void NetLog::DeprecatedRemoveObserver(NetLog::ThreadSafeObserver* observer)
{
    base::AutoLock lock(lock_);

    DCHECK(observers_.HasObserver(observer));
    DCHECK_EQ(this, observer->net_log_);
    observers_.RemoveObserver(observer);
    observer->net_log_ = NULL;
    observer->capture_mode_ = NetLogCaptureMode();
    UpdateIsCapturing();
}

void NetLog::UpdateIsCapturing()
{
    lock_.AssertAcquired();
    base::subtle::NoBarrier_Store(&is_capturing_,
        observers_.might_have_observers() ? 1 : 0);
}

// static
std::string NetLog::TickCountToString(const base::TimeTicks& time)
{
    int64_t delta_time = (time - base::TimeTicks()).InMilliseconds();
    return base::Int64ToString(delta_time);
}

// static
const char* NetLog::EventTypeToString(EventType event)
{
    switch (event) {
#define EVENT_TYPE(label) \
    case TYPE_##label:    \
        return #label;
#include "net/log/net_log_event_type_list.h"
#undef EVENT_TYPE
    default:
        NOTREACHED();
        return NULL;
    }
}

// static
base::Value* NetLog::GetEventTypesAsValue()
{
    std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
    for (int i = 0; i < EVENT_COUNT; ++i) {
        dict->SetInteger(EventTypeToString(static_cast<EventType>(i)), i);
    }
    return dict.release();
}

// static
const char* NetLog::SourceTypeToString(SourceType source)
{
    switch (source) {
#define SOURCE_TYPE(label) \
    case SOURCE_##label:   \
        return #label;
#include "net/log/net_log_source_type_list.h"
#undef SOURCE_TYPE
    default:
        NOTREACHED();
        return NULL;
    }
}

// static
base::Value* NetLog::GetSourceTypesAsValue()
{
    std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
    for (int i = 0; i < SOURCE_COUNT; ++i) {
        dict->SetInteger(SourceTypeToString(static_cast<SourceType>(i)), i);
    }
    return dict.release();
}

// static
const char* NetLog::EventPhaseToString(EventPhase phase)
{
    switch (phase) {
    case PHASE_BEGIN:
        return "PHASE_BEGIN";
    case PHASE_END:
        return "PHASE_END";
    case PHASE_NONE:
        return "PHASE_NONE";
    }
    NOTREACHED();
    return NULL;
}

// static
NetLog::ParametersCallback NetLog::BoolCallback(const char* name, bool value)
{
    return base::Bind(&NetLogBoolCallback, name, value);
}

// static
NetLog::ParametersCallback NetLog::IntCallback(const char* name, int value)
{
    return base::Bind(&NetLogIntCallback, name, value);
}

// static
NetLog::ParametersCallback NetLog::Int64Callback(const char* name,
    int64_t value)
{
    return base::Bind(&NetLogInt64Callback, name, value);
}

// static
NetLog::ParametersCallback NetLog::StringCallback(const char* name,
    const std::string* value)
{
    DCHECK(value);
    return base::Bind(&NetLogStringCallback, name, value);
}

// static
NetLog::ParametersCallback NetLog::StringCallback(const char* name,
    const char* value)
{
    DCHECK(value);
    return base::Bind(&NetLogCharStringCallback, name, value);
}

// static
NetLog::ParametersCallback NetLog::StringCallback(const char* name,
    const base::string16* value)
{
    DCHECK(value);
    return base::Bind(&NetLogString16Callback, name, value);
}

void NetLog::AddEntry(EventType type,
    const Source& source,
    EventPhase phase,
    const NetLog::ParametersCallback* parameters_callback)
{
    if (!IsCapturing())
        return;
    EntryData entry_data(type, source, phase, base::TimeTicks::Now(),
        parameters_callback);

    // Notify all of the log observers.
    base::AutoLock lock(lock_);
    FOR_EACH_OBSERVER(ThreadSafeObserver, observers_, OnAddEntryData(entry_data));
}

BoundNetLog::~BoundNetLog()
{
    liveness_ = DEAD;
}

void BoundNetLog::AddEntry(NetLog::EventType type,
    NetLog::EventPhase phase) const
{
    CrashIfInvalid();

    if (!net_log_)
        return;
    net_log_->AddEntry(type, source_, phase, NULL);
}

void BoundNetLog::AddEntry(
    NetLog::EventType type,
    NetLog::EventPhase phase,
    const NetLog::ParametersCallback& get_parameters) const
{
    CrashIfInvalid();

    if (!net_log_)
        return;
    net_log_->AddEntry(type, source_, phase, &get_parameters);
}

void BoundNetLog::AddEvent(NetLog::EventType type) const
{
    AddEntry(type, NetLog::PHASE_NONE);
}

void BoundNetLog::AddEvent(
    NetLog::EventType type,
    const NetLog::ParametersCallback& get_parameters) const
{
    AddEntry(type, NetLog::PHASE_NONE, get_parameters);
}

void BoundNetLog::BeginEvent(NetLog::EventType type) const
{
    AddEntry(type, NetLog::PHASE_BEGIN);
}

void BoundNetLog::BeginEvent(
    NetLog::EventType type,
    const NetLog::ParametersCallback& get_parameters) const
{
    AddEntry(type, NetLog::PHASE_BEGIN, get_parameters);
}

void BoundNetLog::EndEvent(NetLog::EventType type) const
{
    AddEntry(type, NetLog::PHASE_END);
}

void BoundNetLog::EndEvent(
    NetLog::EventType type,
    const NetLog::ParametersCallback& get_parameters) const
{
    AddEntry(type, NetLog::PHASE_END, get_parameters);
}

void BoundNetLog::AddEventWithNetErrorCode(NetLog::EventType event_type,
    int net_error) const
{
    DCHECK_NE(ERR_IO_PENDING, net_error);
    if (net_error >= 0) {
        AddEvent(event_type);
    } else {
        AddEvent(event_type, NetLog::IntCallback("net_error", net_error));
    }
}

void BoundNetLog::EndEventWithNetErrorCode(NetLog::EventType event_type,
    int net_error) const
{
    DCHECK_NE(ERR_IO_PENDING, net_error);
    if (net_error >= 0) {
        EndEvent(event_type);
    } else {
        EndEvent(event_type, NetLog::IntCallback("net_error", net_error));
    }
}

void BoundNetLog::AddByteTransferEvent(NetLog::EventType event_type,
    int byte_count,
    const char* bytes) const
{
    AddEvent(event_type, base::Bind(BytesTransferredCallback, byte_count, bytes));
}

bool BoundNetLog::IsCapturing() const
{
    CrashIfInvalid();
    return net_log_ && net_log_->IsCapturing();
}

// static
BoundNetLog BoundNetLog::Make(NetLog* net_log, NetLog::SourceType source_type)
{
    if (!net_log)
        return BoundNetLog();

    NetLog::Source source(source_type, net_log->NextID());
    return BoundNetLog(source, net_log);
}

void BoundNetLog::CrashIfInvalid() const
{
    Liveness liveness = liveness_;

    if (liveness == ALIVE)
        return;

    base::debug::Alias(&liveness);
    CHECK_EQ(ALIVE, liveness);
}

} // namespace net
